package cn.monkey.data.mongo;

import cn.monkey.data.entity.BaseEntity;
import cn.monkey.data.repository.BaseEntityCrudRepository;
import cn.monkey.data.repository.BeforeCreateBehavior;
import cn.monkey.data.repository.BeforeUpdateBehavior;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
import org.springframework.data.mongodb.repository.support.SimpleMongoRepository;
import org.springframework.data.util.Streamable;
import org.springframework.util.Assert;

import java.util.List;
import java.util.Optional;

public class SimpleMongoBaseEntityRepository<T extends BaseEntity> extends SimpleMongoCrudRepository<T, String>
        implements BaseEntityCrudRepository<T> {

    /**
     * Creates a new {@link SimpleMongoRepository} for the given {@link MongoEntityInformation} and {@link MongoOperations}.
     *
     * @param metadata        must not be {@literal null}.
     * @param mongoOperations must not be {@literal null}.
     */
    public SimpleMongoBaseEntityRepository(MongoEntityInformation<T, String> metadata, MongoOperations mongoOperations) {
        super(metadata, mongoOperations);
    }

    /**
     * Creates a new {@link SimpleMongoRepository} for the given {@link MongoEntityInformation} and {@link MongoOperations}.
     */
    protected boolean isNew(T entity) {
        return this.entityInformation.isNew(entity);
    }

    @Override
    public <S extends T> List<S> saveAll(Iterable<S> entities) {
        Assert.notNull(entities, "The given Iterable of entities not be null");
        Streamable<S> source = Streamable.of(entities);
        List<S> list = source.stream()
                .peek(s -> {
                    if (this.isNew(s)) {
                        Optional<BeforeCreateBehavior<T>> beforeCreateBehavior = this.mongoRepositoryBehaviorContext.beforeCreateBehavior();
                        beforeCreateBehavior.ifPresent(behavior -> behavior.beforeCreate(s));
                    } else {
                        Optional<BeforeUpdateBehavior<T>> beforeUpdateBehavior = this.mongoRepositoryBehaviorContext.beforeUpdateBehavior();
                        beforeUpdateBehavior.ifPresent(behavior -> behavior.beforeUpdate(s));
                    }
                })
                .toList();
        return super.saveAll(list);
    }

    @Override
    public <S extends T> S save(S entity) {
        if (this.isNew(entity)) {
            return this.insert(entity);
        }
        Optional<BeforeUpdateBehavior<T>> beforeUpdateBehavior = this.mongoRepositoryBehaviorContext.beforeUpdateBehavior();
        beforeUpdateBehavior.ifPresent(behavior -> behavior.beforeUpdate(entity));
        return super.save(entity);
    }


    @Override
    public <S extends T> S insert(S entity) {
        Optional<BeforeCreateBehavior<T>> beforeCreateBehavior = this.mongoRepositoryBehaviorContext.beforeCreateBehavior();
        beforeCreateBehavior.ifPresent(behavior -> behavior.beforeCreate(entity));
        return super.insert(entity);
    }
}
